<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title></title>
		<style type="text/css">
			*{
				margin: 0;
				padding: 0;
			}
			body{
				width: 100vw;
				height: 100vh;
			}
			#topbar{
				height: 100px;
				background-color: #181818;
				display: flex;
				justify-content: flex-start;
				align-items: center;
				color: #fff;
			}
			.btn{
				margin: 4px 10px;
				width: fit-content;
				height: fit-content;
				padding: 6px 20px;
				border: 1px solid #83CEFF;
				border-radius: 50px;
				color: #4ab1ff;
				user-select: none;
				cursor: pointer;
			}
			.btn:hover{
				background-color: #00BFFF;
				color: #fff;
			}
			.btn:active{
				background-color: #23dbff;
			}
			.btn.act{
				background-color: #00BFFF;
				color: #fff;
			}
			.side-btn{
				position: absolute;
				right: 20px;
				top: 30px;
				white-space: nowrap;
				display: flex;
				align-items: center;
				font-size: 12px;
				user-select: none;
			}
			.side-btn .checkbox{
				position: relative;
				width: 40px;
				height: 40px;
				margin: 0 4px;
				border: 1px solid #83CEFF;
				border-radius: 50px;
				color: #4ab1ff;
				line-height: 40px;
				text-align: center;
				padding: 0;
				overflow: hidden;
			}
			.side-btn .checkbox::before{
				content: "";
				position: absolute;
				left: 0;
				top: 0;
				width: 100%;
				height: 100%;
			}
			.side-btn .act{
				background-color: #00BFFF;
				color: #fff;
			}
			#rangeTxt{
				font-size: 18px;
			}
			.width-ctrl{
				width: 240px;
			}
			.width-ctrl>div{
				display: flex;
				margin-bottom: 20px;
			}
			.tip{
				width: fit-content;
				margin: 2px;
				padding: 4px 14px;
				border: 1px solid #aaa;
				background-color: #f1f1f1;
				box-shadow: -2px 2px 2px #00000022;
				border-radius: 60px;
				user-select: none;
				cursor: pointer;
			}
			#canvas1{
				position: absolute;
				left: calc(50% - 300px);
				top: 200px;
				box-shadow: 0 0 0 4px #888888;
			}
		</style>
	</head>
	<body>
		<div id="topbar">
			<div index="0" class="btn act">绘制矩形</div>
			<div index="1" class="btn">绘制圆形</div>
			<div index="2" class="btn">画笔</div>
			<div class="btn clearFn">清空</div>
			<div class="side-btn">
				填充： <input type="color" id="fillColorPicker" value="#93e8ff"/>&nbsp;&nbsp;
				描边： <input type="color" id="borderColorPicker" value="#ababab"/>&nbsp;&nbsp;
				画笔： <input type="color" id="brushColorPicker" value="#ff4762"/>&nbsp;&nbsp;
				<div class="width-ctrl">
					<div>
						描边宽度： <input type="range" oninput="updateRange(value,'stroke')" min="1" max="20" value="4">&nbsp;&nbsp;<div id="strokeRangeTxt">4</div>
					</div>
					<div>
						画笔粗细： <input type="range" oninput="updateRange(value,'brush')" min="1" max="200" value="4">&nbsp;&nbsp;<div id="brushRangeTxt">4</div>
					</div>
				</div>
				<div id="isFillBtn" class="checkbox">填充</div>
				<div id="isStrokeBtn" class="checkbox act">描边</div>
			</div>
		</div>
		<div class="tip" onclick="ctrlZ()">撤销 (Ctrl + Z)</div>
		<canvas id="canvas1" width="600" height="600"></canvas>
		<script type="text/javascript">
			// 声明区
			{
				var canvas = document.querySelector("#canvas1");
				var cx = canvas.getContext("2d");
				var isPress = false;
				var isFill = false;
				var isStroke = true;
				var funIndex = 0;
				var imgData = null;
				var fillColor = "#93e8ff";
				var borderColor = "#c2c2c2";
				var brushColor = "#ff304f"
				var borderWidth = 4;
				var brushSize = 4;
				var start = [];
				var current = [];
				var end = [];
				var linePointList = [];
				var imgStack = [];
				var canvasOffset = [canvas.offsetLeft,canvas.offsetTop];
			}
			// 操作区
			cx.strokeStyle = "#666";
			cx.lineWidth = 4;
			imgStack.push(cx.getImageData(0,0,600,600));
			canvas.onmousedown = function () {
				isPress = true;
				start[0] = event.offsetX;
				start[1] = event.offsetY;
				fillColor = fillColorPicker.value;
				borderColor = borderColorPicker.value;
				brushColor = brushColorPicker.value;
				canvasOffset = [canvas.offsetLeft,canvas.offsetTop];
				linePointList = [];
				linePointList.push([start[0],start[1]]);
				cx.fillStyle = fillColor;
				cx.lineWidth = borderWidth;
				cx.strokeStyle = funIndex == 2 ? brushColor : borderColor;
			}
			window.onmousemove = function () {
				if(isPress){
					// 鼠标拖动时
					current[0] = event.pageX - canvasOffset[0];
					current[1] = event.pageY - canvasOffset[1];
					linePointList.push([current[0],current[1]]);
					cx.clearRect(0,0,600,600);
					imgStack[0] && cx.putImageData(imgStack[imgStack.length-1],0,0,0,0,600,600);
					if(funIndex == 0){ // 开始绘制矩形
						cx.beginPath();
						cx.rect(start[0],start[1],current[0] - start[0],current[1] - start[1]);
						isStroke && cx.stroke();
						isFill && cx.fill();
					}else if(funIndex == 1){ // 开始绘制圆形
						cx.beginPath();
						cx.arc(start[0]+(current[0]-start[0])/2,start[1]+(current[1]-start[1])/2,Math.abs(current[1]-start[1])/2,0,Math.PI*2)
						isStroke && cx.stroke();
						isFill && cx.fill();
					}else if(funIndex == 2){ // 开始绘制线条
						cx.lineWidth = brushSize;
						cx.beginPath();
						cx.moveTo(linePointList[0][0],linePointList[0][1]);
						for(var i = 1; i < linePointList.length; i++){
							cx.lineTo(linePointList[i][0],linePointList[i][1])
						}
						cx.stroke()
					}
				}
			}
			window.onmouseup = function () {
				if(isPress){
					isPress = false;
					end[0] = event.pageX - canvasOffset[0];
					end[1] = event.pageY - canvasOffset[1];
					imgData = cx.getImageData(0,0,600,600);
					imgStack.push(imgData);
				}
			}
			window.onkeypress = function(){
				if(event.key == '\u001a' || event.ctrlKey == true && event.keyCode == 90){
					ctrlZ()
				}
			}
			function ctrlZ(){
				if(imgStack.length > 1){
					imgStack.pop();
					cx.clearRect(0,0,600,600);
					cx.putImageData(imgStack[imgStack.length-1],0,0,0,0,600,600);
				}
			}
			topbar.onclick = function () {
				if(event.target.getAttribute("index") == undefined){
					if(event.target.classList.contains("clearFn")){
						cx.clearRect(0,0,600,600);
						imgStack = [];
					}
				}else if(event.target.classList.contains("btn")){
					Array.from(topbar.children).forEach(el=>{
						el.classList.remove("act");
					})
					event.target.classList.add("act");
					var index = parseInt(event.target.getAttribute("index"));
					funIndex = index;
				}
			}
			topbar.onmousemove = function(){event.stopPropagation()}
			topbar.onmouseup = function(){event.stopPropagation()}
			isFillBtn.onclick = function() {
				if(isFill){
					isFill = false;
					this.classList.remove("act")
				}else{
					isFill = true;
					this.classList.add("act")
				}
			}
			isStrokeBtn.onclick = function() {
				if(isStroke){
					isStroke = false;
					this.classList.remove("act")
				}else{
					isStroke = true;
					this.classList.add("act")
				}
			}
			fillColorPicker.oninput = function(){
				fillColor = this.value
			}
			borderColorPicker.oninput = function() {
				borderColor = this.value
			}
			brushColorPicker.oninput = function() {
				brushColor = this.value
			}
			function updateRange(v,type){
				if(type == 'stroke'){
					strokeRangeTxt.innerText = v;
					borderWidth = parseInt(v)
				}else if(type == 'brush'){
					brushRangeTxt.innerText = v;
					brushSize = parseInt(v)
				}
				
			}
			
		</script>
	</body>
</html>